#include "ccmdparse_v2.h"
#include "serversetting.h"
#include "clog.h"
#include "knx/knx_interface.h"
#include "knx/qt_knx.h"
#include "cmd_parse/cmd.h"
#include "knx/cmei.h"
#include <QThread>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QFile>
#include <QTime>
#include <QDateTime>
#include "cudpcmd.h"
#include "xml/pickouttag.h"


CCmdParse_v2::CCmdParse_v2(QObject *parent) :
    QObject(parent)
{
//    ttgIps = new CTTGIpsConnector(this);

//    ttgIps->setNetWork(ServerSetting::defaultSetting()->_ip,ServerSetting::defaultSetting()->_port.toUInt());
//    ttgIps->setLogInfo(ServerSetting::defaultSetting()->_user,ServerSetting::defaultSetting()->_password);

//    ttgIps->setNetWork("192.168.1.211",5006);
//    ttgIps->setLogInfo("Hinwo123","Hinwo0");

//    connect(ttgIps,SIGNAL(s_gateWayConnectState(int)),this,SLOT(doGateWayStateUpdate(int)));
//    connect(ttgIps,SIGNAL(s_valueNotify(void *,QByteArray ,void *)),this,SLOT(doGateWayValueUpdate(void *,QByteArray ,void *)));

    locolIps = new CLocolIPS(this);
    CUdpCmd::cntDelegate = locolIps;

    //server will record the gateway connect state
    //connect(locolIps,SIGNAL(s_gateWayConnectState(int)),this,SLOT(doGateWayStateUpdate(int)));
    connect(locolIps,SIGNAL(s_valueNotify(void *,QByteArray ,void *)),this,SLOT(doGateWayValueUpdate(void *,QByteArray ,void *)));
    connect(locolIps,SIGNAL(s_valueNotify_debug(void *,QString ,void *)),this,SLOT(doGateWayValueUpdateDebug(void *,QString ,void *)));

    connect(locolIps,SIGNAL(s_recordMsg(QString)),this,SLOT(doRecordUpdate(QString)));

    _refreshTimer.setInterval(200);
    connect(&_refreshTimer,SIGNAL(timeout()),this,SLOT(doRefreshObject()));
    //cmd signals & slots

    connect(&_testStatusTimer,SIGNAL(timeout()),this,SLOT(doStatusTest()));
    //_testStatusTimer.start(10*1000);

    _gLog = CLog::getSingle();
}

CCmdParse_v2::~CCmdParse_v2()
{
    projectDestroy();
}

void CCmdParse_v2::projectInit(QString jsonPath)
{
}

bool CCmdParse_v2::projectContext(QByteArray xml)
{
    projectDestroy();
    QString rxml;
    QJsonDocument domTree = QJsonDocument::fromJson(xml);

    if (domTree.isNull() || domTree.isEmpty())
    {
        qDebug() << "Json 格式错误: " << xml;
        return false;
    }

    if(domTree.isObject())
    {
        QJsonObject jobj = domTree.object();
        rxml = jobj.value("data").toString();
    }

    if(rxml.isEmpty())
    {
        qDebug() << "v2 XML 读取错误" ;
        return false;
    }

    QList<QDomNodeList> xmlList = PickOutTag::FetchXMLDeviceNode(rxml.toLocal8Bit());

    for(int i=0;i<xmlList.size();i++)
    {
        QDomNodeList list = xmlList.at(i);
        for(int j=0;j<list.size();j++)
        {
             QDomElement e = list.at(j).toElement();
             qDebug() << "projectContex2: " << e.tagName();


             Xml_DEVICE_OBJECT *jo = new Xml_DEVICE_OBJECT(e,(void *)locolIps);
             _regObjMap.insert(jo->_id,jo);

             //_audioControl.insertDeviceObject(jo);
             addDevice2audioControl(jo);

             qDebug() << "pid - obj " << jo->_id;

             QThread::msleep(20);
        }
    }

    if(_regObjMap.size() > 0)
    {
        setUpKnxObjs();
        return true;
    }


    return false;
}

void CCmdParse_v2::addDevice2audioControl(Xml_DEVICE_OBJECT *j)
{
     DEVICE_AUDIOCONTROL type = UNKNOW;
     QString device_name = j->_type;
     QString ops;

     qDebug() << "Audio:  addDevice2audioControl " << j->_name << " - " << j->_type;

     if(device_name == "object")
     {
         type = LIGHT;
         ops = DEVICE_ACTION_SWITCH;
     }
     else if(device_name == "dim")
     {
         type = LIGHT;
         ops = QString("%1;%2").arg(DEVICE_ACTION_SWITCH).arg(DEVICE_ACTION_DIM);
     }
     else if(device_name == "scene")
     {
         if(j->_name.contains("门锁"))
         {
             type = DOOR;
             ops = DEVICE_SWITCH_ON;
         }else{
            type = SCENE;
            ops = DEVICE_ACTION_CLICK;
         }
     }
     else if(device_name == "window")
     {
         type = CURTAIN;
         ops = DEVICE_ACTION_MOVE;
     }
     else if(device_name == "air")
     {
         type = AIR;
         ops = QString("%1;%2;%3;%4").arg(DEVICE_ACTION_SWITCH).arg(DEVICE_ACTION_TEMP)
               .arg(DEVICE_ACTION_MODE).arg(DEVICE_ACTION_WIND);
     }
     else if(device_name == "label")
     {
         type = LABEL;
         ops = DEVICE_ACTION_READ;
     }
     else if(device_name == "tv-remote")
     {
         type = TV_REMOTE;
         ops = DEVICE_ACTION_CLICK;
     }
     else if(device_name == "music-remote")
     {
         type = MUSIC_REMOTE;
         ops = DEVICE_ACTION_CLICK;
     }

     if(type!= UNKNOW){
         qDebug() << "Audio:  project add " << j->_name << " - " <<type;
        _audioCommunicate.addDevice(j->_id,j->_name,type,ops);
     }
     else{
        qDebug() << "Audio:  project add error " << j->_name << " - " << device_name;
     }
}

void CCmdParse_v2::projectDestroy()
{
    locolIps->unRegisterAll();

    if(_regObjMap.size()!=0)
    {
        QMap<QString,Xml_DEVICE_OBJECT *>::Iterator it = _regObjMap.begin();
        while (it!=_regObjMap.end()) {
            Xml_DEVICE_OBJECT *o = it.value();
            if(o)
                delete o;

            it++;
        }
    }

    _regObjMap.clear();
    _listReadAfterWrite.clear();
    _listRefresh.clear();
    _allDeviceId.clear();

    //_audioControl.clearMap();
    _audioCommunicate.clearDevice();
}

void CCmdParse_v2::setUpKnxObjs()
{
    QMap<QString,Xml_DEVICE_OBJECT *>::Iterator it = _regObjMap.begin();
    while (it!=_regObjMap.end()) {
        Xml_DEVICE_OBJECT *o = it.value();
        _allDeviceId.append(o->_id);
        if(o && o->_regList)
        {
            for(int i=0;i<o->_regList->size();i++)
            {
                DEVICE_OBJECT *obj = o->_regList->at(i);
                locolIps->registeObject(obj,(void *)o);
            }
        }

        it++;
    }

    //refresh all object status
    if(ServerSetting::defaultSetting()->_reload_knx != "0")
    {
        it = _regObjMap.begin();
        while (it!=_regObjMap.end()) {
            Xml_DEVICE_OBJECT *o = it.value();
            if(o && o->_regList)
            {
                _listRefresh.append(o);
                //o->doRead();
            }

            it++;
        }
    }

    //debug
    refreshStatus();

//    DEVICE_OBJECT *obj = new DEVICE_OBJECT();
//    obj->_address = "1/5/58";
//    obj->_data_type = ONE_BIT_VALUE;
//    obj->_data_define = UNSIGNED_INT;
//    cmei_set_group_address(obj->_ga_ia,obj->_address);

//    ttgIps->registeObject(obj,NULL);

//    obj = new DEVICE_OBJECT();
//    obj->_address = "1/5/56";
//    obj->_data_type = ONE_BIT_VALUE;
//    obj->_data_define = UNSIGNED_INT;
//    cmei_set_group_address(obj->_ga_ia,obj->_address);

//    ttgIps->registeObject(obj,NULL);
}

void CCmdParse_v2::refreshStatus()
{
    if(_refreshTimer.isActive())
    {
        qDebug() << "refreshStatus still running...";
        return;
    }

    _listReadAfterWrite = QList<Xml_DEVICE_OBJECT *>(_listRefresh);

    if(!_refreshTimer.isActive())
        _refreshTimer.start();
}

void CCmdParse_v2::doRefreshObject()
{
    if(_listReadAfterWrite.size()>0)
    {
        qDebug() << "timer read status:" << _listReadAfterWrite.size();
        Xml_DEVICE_OBJECT *o = _listReadAfterWrite.takeFirst();
        o->doRead();
    }
    else
        _refreshTimer.stop();
}

void CCmdParse_v2::doGateWayStateUpdate(int state)
{
    QString msg = QString("@%1:%2:%3:%4:%5:%6")
                        .arg(0)
                        .arg(0)
                        .arg("gateway")
                        .arg("status")
                        .arg(ServerSetting::defaultSetting()->_ip)
                        .arg(state);

    emit s_JsonReport(msg);

}

void CCmdParse_v2::doRecordUpdate(QString value)
{
    _gLog->logOutput(value);
    //qDebug() << "doRecordUpdate:" << str;
}

void CCmdParse_v2::doGateWayValueUpdateDebug(void *devObj,QString value,void *userdata)
{
    DEVICE_OBJECT *o = (DEVICE_OBJECT *)devObj;
    QString sv = value;
    QString action = "";
    if(userdata)
    {
        Xml_DEVICE_OBJECT *jo = (Xml_DEVICE_OBJECT *)userdata;
        action = jo->actionFromStatus(o->_address,sv);
        sv = jo->valueCheck(action,sv);

        QString msg = QString("{\"Gate_SERIAL\":\"%4\", \"Gate_STATUS\": { \"id\": \"%1\",\"action\": \"%2\",\"value\":\"%3\" } }")
                .arg(jo->_id).arg(action).arg(sv).arg(ServerSetting::defaultSetting()->_serial);;

        emit s_deviceJsonReport(jo->_id+"-"+action,msg);
    }
}

void CCmdParse_v2::doGateWayValueUpdate(void *devObj,QByteArray value,void *userdata)
{
    DEVICE_OBJECT *o = (DEVICE_OBJECT *)devObj;
    QString sv = DEVICE_OBJECT::KnxDataToQString(o->_data_type,o->_data_define,(_u8 *)value.data(),value.size());
    QString action = "";
    if(userdata)
    {
        Xml_DEVICE_OBJECT *jo = (Xml_DEVICE_OBJECT *)userdata;
        action = jo->actionFromStatus(o->_address,sv);
        sv = jo->valueCheck(action,sv);

        QString msg = QString("{\"Gate_SERIAL\":\"%4\",\"Gate_STATUS\": { \"id\": \"%1\",\"action\": \"%2\",\"value\":\"%3\" } }")
                .arg(jo->_id).arg(action).arg(sv).arg(ServerSetting::defaultSetting()->_serial);

        emit s_deviceJsonReport(jo->_id+"-"+action,msg);
    }

}

void CCmdParse_v2::doStatusTest()
{
    anayseJsonData("{ \"Gate_VOICE\": { \"word\":\"打开吧台吊灯\"}}");

//    QList<QString> list = _regObjMap.keys();
//    int size = list.size();
//    if(size>0)
//    {
//        int r = rand() % size;
//        Xml_DEVICE_OBJECT *jo = _regObjMap.value(list.at(r));
//       if(jo->_type == "switch" || jo->_type == "dim")
//        {
//            QString sv = "on";
//            if(rand()%2 != 0)
//                sv = "off";

//            QString action = sv;
//            if(jo->_type == "dim")
//            {
//                action = "value";
//               sv = QString("%1").arg(rand()%200);
//            }

//            QString msg = QString("%1:%2:%3:%4:%5:%6")
//                                .arg(jo->_id)
//                                .arg(jo->_id)
//                                .arg(jo->_type)
//                                .arg(action)
//                                .arg(list.at(r))
//                                .arg(sv);

//           //qDebug() << "Do emit test update";^M
//           emit s_deviceJsonReport(jo->_id,msg);
//        }

//   }

}

void CCmdParse_v2::runningCheck()
{
    if(!locolIps->init)
        locolIps->resetChannel();
}

/*
 *  设备状态上传更新  all web app none
    "{ \"Gate_UPDATE\":\"all\" }"

*/
bool CCmdParse_v2::anayseXmlData(QByteArray xmlData)
{
    QDomDocument domTree;
    QString errorMsg ;
    int errorLine;
    int errorColumn;

    if (!domTree.setContent( xmlData,false, &errorMsg , &errorLine , &errorColumn ) )
    {
        qDebug() << "XML格式错误,msg: " << errorMsg << "line " << errorLine;
        qDebug() << "XML data:" <<  QString(xmlData);
        return false;
    }

    QDomElement ele = domTree.documentElement();
    QString cmdStr = ele.tagName();

    qDebug() << "Gate Way Tcp read:" << xmlData;

    QWriteLocker locker(&lock);

    if(cmdStr == SET_DEV)
    {
        setDevControl(ele);
    }
    else if(cmdStr == GET_DEV)
    {
        getDevControl(ele);
    }

    return true;
}

void CCmdParse_v2::setDevControl(QDomElement element)
{
    DATA_TYPE value_type = (DATA_TYPE)element.attributeNode("value_type").value().toUInt();
    DATA_DEFINE value_define = (DATA_DEFINE)element.attributeNode("value_define").value().toUInt();
    QString value_addr = element.attributeNode("address").value();
    QString value = element.attributeNode("value").value();

    if(!value_addr.isEmpty())
    {
        int data_len = 0;
        if((data_len = DEVICE_OBJECT::KnxQStringToData(value,value_type,value_define,(_u8 *)_data_buf)) == -1)
        {
            qDebug() << "SetDev ret='-1' msg='error data args'" ;
            return ;
        }

        cmei_set_group_address(_addr_buf,value_addr.toLocal8Bit());
        locolIps->ttg_write_device(_addr_buf,value_type,_data_buf,data_len);
    }
    else
    {
        qDebug() << "MSG_ADDR isEmpty" ;
    }

}

void CCmdParse_v2::getDevControl(QDomElement element)
{
    QString value_addr = element.attributeNode("address").value();
    int id = element.attributeNode("id").value().toInt();

    if(!value_addr.isEmpty())
    {
        cmei_set_group_address(_addr_buf,value_addr.toLocal8Bit());
        locolIps->ttg_read_status(_addr_buf);
    }
}

// json
///////////////////////////////////////////////////////////////////////

QByteArray CCmdParse_v2::anayseJsonData(QByteArray jsonData)
{
    QJsonDocument domTree = QJsonDocument::fromJson(jsonData);

    if (domTree.isNull() || domTree.isEmpty())
    {
        qDebug() << "Json 格式错误: " << jsonData;
        return "";
    }

    if(domTree.isObject())
    {
        QJsonObject jobj = domTree.object();
        QString cmdStr = "";

        QJsonObject::Iterator it = jobj.begin();
        while (it != jobj.end()) {
            cmdStr = it.key().trimmed();

            if(it.value().type() == QJsonValue::Object)
            {
                QJsonObject obj = it.value().toObject();

                if(cmdStr == SET_DEV)
                {
                    setDevControlJson(obj);
                }
                else if(cmdStr == GET_DEV)
                {
                    getDevControlJson(obj);
                }
                else if(cmdStr == UPDATE_DEV)
                {
                    updateDevJson(obj);
                }
                else if(cmdStr == SET_VOICE)
                {
                    QString vc = updateVoiceJson(obj);
                    QString ret_msg = QString("{\"Gate_VOICE\":{\"word\":\"%1\",\"from\":\"GATEWAY\"} }").arg(vc);
                    return ret_msg.toLocal8Bit();
                }
                else if(cmdStr == MSG_DEV){
                    return setDevMsg(obj).toLocal8Bit();
                }
            }

            it++;
        }
    }

    return "";
}

QString CCmdParse_v2::setDevMsg(QJsonObject element)
{
    QString cmd = element.value("cmd").toString();
    QStringList args = element.value("args").toString().split(",");

    bool r = _execCmd.doCmd(cmd,args);
    if(r){
        return cmd;
    }else{
        return "fail";
    }
}

QString CCmdParse_v2::updateVoiceJson(QJsonObject element)
{
    QString word;
    int type,caller;

    type = element.value("type").toInt();
    caller = element.value("caller").toInt();
    word = element.value("word").toString();

    DEVICE_OPERATION * ret_audio = _audioCommunicate.wordAnalyse(word);
    if(ret_audio!=nullptr){
        if(!ret_audio->_audio_action_dev_all)
        {
            Xml_DEVICE_OBJECT * o = _regObjMap.value(ret_audio->_id);
            if(o!=nullptr && o->_usrcontrol!=nullptr)
                o->_usrcontrol->execDeviceAction(ret_audio->_audio_operation,ret_audio->_audio_value,ret_audio->_audio_calc);
        }
        else
        {
            foreach (QString id, ret_audio->_id_all) {
                Xml_DEVICE_OBJECT * o = _regObjMap.value(id);
                if(o!=nullptr && o->_usrcontrol!=nullptr)
                    o->_usrcontrol->execDeviceAction(ret_audio->_audio_operation,ret_audio->_audio_value,ret_audio->_audio_calc);
            }

        }

        return "已执行相关指令";
    }
    else{
        return "无法解析指令或没有相关设备";
    }
}

void CCmdParse_v2::setDevControlJson(QJsonObject element)
{
    QString id,action,value;
    id = element.value("id").toString();
    action = element.value("action").toString();
    value = element.value("value").toString();

    QStringList idlist = id.split(",");
    int find_obj = -1;
    for(int i=0;i<idlist.size();i++)
    {
        Xml_DEVICE_OBJECT *obj = _regObjMap.value(idlist.at(i),NULL);
        if(obj!=NULL)
        {
            find_obj = 0;
            obj->doAction(action,value);
        }
    }

    QString msg = QString("{\"Gate_ACK\": { \"id\": \"%1\",\"action\": \"%2\",\"value\":\"%3\",\"result\":\"%4\" },\"Gate_SERIAL\":\"%5\"}")
            .arg(id).arg(action).arg(value).arg(find_obj).arg(ServerSetting::defaultSetting()->_serial);

    emit s_deviceJsonReport(id+"-"+action,msg);
}


void CCmdParse_v2::updateDevJson(QJsonObject element)
{
    QString mode = element.value("mode").toString();
    emit s_updateMode(mode);
}

int CCmdParse_v2::doAudioParse(QByteArray bytes)
{
    return _audioControl.doParseVoice(bytes);
}

void CCmdParse_v2::getDevControlJson(QJsonObject element)
{
    QString idstr = element.value("id").toString();
    QStringList idlist = idstr.split(",");
    for(int i=0;i<idlist.size();i++)
    {
        QString id = idlist.at(i);
        if(id.toInt() == -1)
        {
            emit s_controlCmd(CMD_CONTROL_READ_ALL);
            break;
        }
        else if(id.toInt() == 0)
        {
            QString msg = QString("{\"Gate_INFO\": { \"version\": \"%1\",\"serial\":\"%2\",\"ip\":\"%3\"} }")
                    .arg(g_version).arg(ServerSetting::defaultSetting()->_serial).arg(ServerSetting::defaultSetting()->getHostIpString("eth"));

            emit s_deviceJsonReport("0-read",msg);
            break;
        }
        else 
	{
            Xml_DEVICE_OBJECT *obj = _regObjMap.value(id);
            if(obj)
            {
                qDebug() << "Json read cmd: " << obj->_id;
                obj->doRead();
		QThread::msleep(30);
            }
            else
            {
                qDebug() << "Json read cmd: not foud object id " << id;
            }
        }
    }

}
